package com.sunday.redis.lua.core;

import com.sunday.common.core.gson.GsonUtils;

import java.lang.reflect.Type;
import java.util.Collection;

/**
 * redis lua
 *
 * @author zsy
 * @since 2022/10/17
 */
public interface RedisLuaOperations {

    Long incrementWithLimit(String key, long upper);

    /**
     * 指定数量累加, 并判定上限
     * 场景: 用户部分功能的数量限制，防止设置超限
     *
     * @param key
     * @param delta 数量
     * @param upper 上限
     * @return {@link Long} 到达上限返回null，否则返回累加后的值
     */
    Long incrementWithLimit(String key, long delta, long upper);

    Long decrementWithFloor(String key);

    Long decrementWithFloor(String key, long delta);

    /**
     * 指定数量扣减, 并判定下限
     * 场景: 用户余额指定数量扣减
     *
     * @param key
     * @param delta 数量
     * @param lower 下限
     * @return {@link Long} 到达下限返回null，否则返回递减后的值
     */
    Long decrementWithFloor(String key, long delta, long lower);

    Long incrementWithinPeriod(String key, long upper, long timePeriodInSeconds);

    /**
     * 在指定时间范围内按照指定数量累加，并判定上限
     * 场景: 分布式消息递送的频次限制，请求的TPS限制
     *
     * @param key
     * @param delta               数量
     * @param upper               上限
     * @param timePeriodInSeconds 过期时间/S
     * @return {@link Long} 返回应休眠的时间（ms毫秒），返回0未超限无需休眠
     */
    Long incrementWithinPeriod(String key, long delta, long upper, long timePeriodInSeconds);

    Boolean easyIncrementWithinPeriod(String key, long upper, long expirationTimeS);

    /**
     * 时间范围内超限判定 (简易版，不进行TTL控制)
     * 场景: 分布式消息递送的频次限制，请求的TPS限制
     *
     * @param key
     * @param delta               数量
     * @param upper               上限
     * @param timePeriodInSeconds 过期时间/S
     * @return {@link Boolean} 返回上限情况，true正常累加，false超限
     */
    Boolean easyIncrementWithinPeriod(String key, long delta, long upper, long timePeriodInSeconds);

    /**
     * 在期限内累加，返回当前计数数量
     * 场景: 仅判定累加数量，不进行额外的控制，其他由业务自行判定
     *
     * @param key
     * @param timePeriodInSeconds
     * @return {@link Long} 返回当前计数数量
     */
    Long incrWithinPeriod(String key, long timePeriodInSeconds);

    Boolean decrementWithinPeriod(String key);

    Boolean decrementWithinPeriod(String key, long limitTimeMS);

    Boolean decrementWithinPeriod(String key, long lower, long limitTimeMS);

    /**
     * 在允许操作的时间范围内进行扣减，并验证下限
     * 场景: 分布式消息递送的频次,发生异常时,计数归还动作
     *
     * @param key
     * @param delta             数量
     * @param lower             下限
     * @param maximumTimeMillis 限制时间范围/MS
     * @return {@link Boolean} 返回下限情况，true正常递减，false超限
     */
    Boolean decrementWithinPeriod(String key, long delta, long lower, long maximumTimeMillis);

    /**
     * 分时段速率控制 (分钟/小时/天)
     * 使用场景：在指定时间内，手机号码超过最大次数，不考虑次数累加带来的影响，并返回当前计数数量
     *
     * @param key
     * @param maxMinute 每分钟上限
     * @param maxHour   每小时上限
     * @param maxDay    每天上限
     * @return {@link Long} 0 未超限 1 每分钟 2 每小时 3 每天
     */
    Long calculateSegmentRate(String key, long maxMinute, long maxHour, long maxDay);

    /**
     * 递增并判定上限，每次都刷新过期时间
     * 场景：下载计数，每次刷新过期时间
     *
     * @param key
     * @param maxLimit    上限
     * @param expireInSec 过期时间/S
     * @return {@link Boolean} 返回是否超限，true未超限，false超限
     */
    Boolean incrementAndCheckMaxWithRefresh(String key, long maxLimit, long expireInSec);

    /**
     * 更新值，不更新过期时间
     * 场景： 仅更新值内容
     *
     * @param key
     * @param value       重置值
     * @param expireInSec 过期时间/S
     * @return {@link Long} 返回剩余过期时间(ms毫秒)
     */
    <T> Long updateValueKeepTTL(String key, T value, long expireInSec);

    /**
     * 值相同刷新过期时间（key 不存在设置失败）
     * 场景： 延长数据的存活时间，排他锁能力
     *
     * @param key
     * @param value
     * @param expireInSec 过期时间/S
     * @return {@link Boolean} 返回是否刷新成功，true刷新成功，false刷新失败
     */
    <T> Boolean refreshExpiryIfValueMatch(String key, T value, long expireInSec);

    /**
     * https://redis.io/commands/del/
     * DEL key [key ...]
     */
    Long delete(Collection<String> keys);

    Boolean delete(String key);

    /**
     * https://redis.io/commands/set/
     * SET key value [NX | XX] [GET] [EX seconds | PX milliseconds | EXAT unix-time-seconds | PXAT unix-time-milliseconds | KEEPTTL]
     * <p>
     * EX seconds -- 设置指定的过期时间，以秒为单位。
     * PX 毫秒——设置指定的过期时间，以毫秒为单位。
     * EXAT timestamp-seconds -- 设置密钥过期的指定 Unix 时间，以秒为单位。
     * PXAT timestamp-milliseconds -- 设置密钥过期的指定 Unix 时间，以毫秒为单位。
     * NX-- 仅当密钥不存在时才设置它。
     * XX-- 仅当密钥已存在时才设置它。
     * KEEPTTL-- 保留与密钥关联的生存时间。
     * GET-- 返回存储在 key 中的旧字符串，如果 key 不存在，则返回 nil。SET如果存储在 key 的值不是字符串，则返回并中止错误。
     * <p>
     * 从 Redis 版本 2.6.12 开始：添加了、EX和PX选项。NXXX
     * 从 Redis 版本 6.0.0 开始：添加了该KEEPTTL选项。
     * 从 Redis 版本 6.2.0 开始：添加了GET,EXAT和PXAT选项。
     * 从 Redis 版本 7.0.0 开始：允许NX和GET选项一起使用。
     *
     * @param key
     * @param value
     */
    <T> void set(String key, T value);

    /**
     * SET key value NX EX seconds
     *
     * @param key
     * @param value
     * @param expireInSec
     * @return {@link Boolean}
     */
    <T> Boolean setIfAbsent(String key, T value, long expireInSec);

    /**
     * SET key value XX EX seconds
     *
     * @param key
     * @param value
     * @param expireInSec
     * @return {@link Boolean}
     */
    <T> Boolean setIfPresent(String key, T value, long expireInSec);

    /**
     * https://redis.io/commands/setex/
     * SETEX key seconds value
     *
     * @param key
     * @param value
     * @param expireInSec
     */
    <T> void setEx(String key, T value, long expireInSec);

    /**
     * https://redis.io/commands/get/
     * GET key
     *
     * @param key
     * @return {@link String}
     */
    String get(String key);

    <T> T get(String key, Class<T> clazz);

    <T> T get(String key, Type type);

    /**
     * https://redis.io/commands/ttl/
     * TTL key
     *
     * @param key
     * @return {@link Long}
     */
    Long ttl(String key);

    /**
     * https://redis.io/commands/pttl/
     * PTTL key
     *
     * @param key
     * @return {@link Long}
     */
    Long pTtl(String key);

    default <T> T fromJson(String json, Class<T> clazz) {
        return GsonUtils.DEFAULT.fromJson(json, clazz);
    }

    default <T> T fromJson(String json, Type type) {
        return GsonUtils.DEFAULT.fromJson(json, type);
    }

    default String toJson(Object obj) {
        return GsonUtils.DEFAULT.toJson(obj);
    }

    /**
     * gson 处理 String类型 会自动添加 ""
     * 因此采用 String类型处理处理 String.valueOf 进行输出
     */
    default <T> String toString(T obj) {
        assert obj != null : "obj must not be null";
        return obj instanceof String ? String.valueOf(obj) : toJson(obj);
    }

}
